//* ////////////////////////////////////////////////////////////////////////////// */
//*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//        Copyright (c) 2010 - 2013 Intel Corporation. All Rights Reserved.
//
//
//*/

#ifndef __SAMPLE_PIPELINE_TRANSCODE_H__
#define __SAMPLE_PIPELINE_TRANSCODE_H__

#include "vm/atomic_defs.h"
#include "vm/thread_defs.h"

#include <memory>
#include <vector>
#include <list>

#include "sample_defs.h"
#include "sample_utils.h"
#include "base_allocator.h"
#include "rotate_plugin_api.h"
#include "mfx_multi_vpp.h"

#include "mfxvideo.h"
#include "mfxvideo++.h"
#include "mfxmvc.h"
#include "mfxjpeg.h"

// =========== ffmpeg muxer integration ============
#define MAXSPSPPSBUFFERSIZE 1000  // Arbitrary size... but 1000 should be enough...
// =========== ffmpeg muxer integration end ============
   
namespace TranscodingSample
{
    extern mfxU32 MSDK_THREAD_CALLCONVENTION ThranscodeRoutine(void   *pObj);

    enum PipelineMode
    {
        Native = 0,        // means that pipeline is based depends on the cmd parameters (decode/encode/transcode)
        Sink,              // means that pipeline makes decode only and put data to shared buffer
        Source             // means that pipeline makes vpp + encode and get data from shared buffer
    };


    struct sInputParams
    {
        sInputParams();
        void Reset();
        // session parameters       
        bool         bIsJoin;
        mfxPriority  priority;
        // common parameters
        mfxIMPL libType;  // Type of used mediaSDK library
        bool   bIsPerf;   // special performance mode. Use pre-allocated bitstreams, output 

        mfxU32 EncodeId; // type of output coded video
        mfxU32 DecodeId; // type of input coded video

        msdk_char  strSrcFile[MSDK_MAX_FILENAME_LEN]; // source bitstream file
        msdk_char  strDstFile[MSDK_MAX_FILENAME_LEN]; // destination bitstream file

		std::vector<msdk_char*> srcFileBuff;
		std::vector<msdk_char*> dstFileBuff;
        
        // specific encode parameters 
        mfxU16 nTargetUsage;
        mfxF64 dFrameRate;
        mfxU32 nBitRate;
        mfxU16 nQuality; // quality parameter for JPEG encoder
        mfxU16 nDstWidth;  // destination picture width, specified if resizing required
        mfxU16 nDstHeight; // destination picture height, specified if resizing required
        
        bool bEnableDeinterlacing;

        mfxU16 nAsyncDepth; // asyncronous queue

        PipelineMode eMode;

        mfxU32 MaxFrameNumber; // maximum frames for transcoding

        mfxU16 nSlices; // number of slices for encoder initialization

        // MVC Specific Options
        bool   bIsMVC; // true if Multi-View-Codec is in use
        mfxU32 numViews; // number of views for Multi-View-Codec

        mfxU16 nRotationAngle; // if specified, enables rotation plugin in mfx pipeline
        msdk_char strPluginDLLPath[MSDK_MAX_FILENAME_LEN]; // plugin dll path and name

		// =========== ffmpeg muxer integration ============
		mfxU32 CodecId;
		bool bMux;
		bool bMuxMkv;
		// =========== ffmpeg muxer integration end ============
    };

    struct ExtendedSurface
    {
        mfxFrameSurface1 *pSurface;
        mfxSyncPoint      Syncp;
    };

    struct ExtendedBS
    {
        ExtendedBS():IsFree(true), Syncp(NULL)
        {
            MSDK_ZERO_MEMORY(Bitstream);
        };
        mfxBitstream   Bitstream;
        mfxSyncPoint   Syncp;
        bool           IsFree;
    };

    class ExtendedBSStore
    {
    public:
        explicit ExtendedBSStore(mfxU32 size)
        {
            m_pExtBS.resize(size);
        };
        virtual ~ExtendedBSStore()
        {
            for (mfxU32 i=0; i < m_pExtBS.size(); i++)
                MSDK_SAFE_DELETE_ARRAY(m_pExtBS[i].Bitstream.Data);
            m_pExtBS.clear();

        };
        ExtendedBS* GetNext()
        {
            for (mfxU32 i=0; i < m_pExtBS.size(); i++)
            {
                if (m_pExtBS[i].IsFree)
                {
                    m_pExtBS[i].IsFree = false;
                    return &m_pExtBS[i];
                }
            }
            return NULL;
        };
        void Release(ExtendedBS* pBS)
        {
            for (mfxU32 i=0; i < m_pExtBS.size(); i++)
            {
                if (&m_pExtBS[i] == pBS)
                {
                    m_pExtBS[i].IsFree = true;
                    return;
                }
            }
            return;
        };
    protected:
        std::vector<ExtendedBS> m_pExtBS;

    private:
        DISALLOW_COPY_AND_ASSIGN(ExtendedBSStore);
    };

    class CTranscodingPipeline;
    // thread safety buffer heterogeneous pipeline
    // only for join sessions
    class SafetySurfaceBuffer
    {
    public:
        struct SurfaceDescriptor
        {
            ExtendedSurface   ExtSurface;
            mfxU32            Locked;
        };

        SafetySurfaceBuffer(SafetySurfaceBuffer *pNext);
        virtual ~SafetySurfaceBuffer();

        void              AddSurface(ExtendedSurface Surf);
        mfxStatus         GetSurface(ExtendedSurface &Surf);
        mfxStatus         ReleaseSurface(mfxFrameSurface1* pSurf);
        
        SafetySurfaceBuffer               *m_pNext;

    protected:
        
        MSDKMutex                 m_mutex;
        std::list<SurfaceDescriptor>       m_SList;
    private:
        DISALLOW_COPY_AND_ASSIGN(SafetySurfaceBuffer);
    };

    // External bitstream processing support
    class BitstreamProcessor
    {
    public:
        BitstreamProcessor() {};
        virtual ~BitstreamProcessor() {};
        virtual mfxStatus PrepareBitstream() = 0;
        virtual mfxStatus GetInputBitstream(mfxBitstream **pBitstream) = 0;
        virtual mfxStatus ProcessOutputBitstream(mfxBitstream* pBitstream) = 0;
    };

    class FileBitstreamProcessor : public BitstreamProcessor
    {
    public:
        FileBitstreamProcessor();
        virtual ~FileBitstreamProcessor();
        virtual mfxStatus Init(msdk_char  *pStrSrcFile, msdk_char  *pStrDstFile);
		virtual mfxStatus Init(TranscodingSample::sInputParams * InputParams);
        virtual mfxStatus PrepareBitstream() {return MFX_ERR_NONE;}
        virtual mfxStatus GetInputBitstream(mfxBitstream **pBitstream);
        virtual mfxStatus ProcessOutputBitstream(mfxBitstream* pBitstream);
    
    //protected:
        std::auto_ptr<CSmplBitstreamReader> m_pFileReader;
        // for performance options can be zero
        std::auto_ptr<CSmplBitstreamWriter> m_pFileWriter;
        mfxBitstream m_Bitstream;
    private:
        DISALLOW_COPY_AND_ASSIGN(FileBitstreamProcessor);
    };


    // Bitstream is external via BitstreamProcessor
    class CTranscodingPipeline
    {
    public:
        CTranscodingPipeline();
        virtual ~CTranscodingPipeline();

        virtual mfxStatus Init(sInputParams *pParams, 
                               MFXFrameAllocator *pMFXAllocator,
                               void* hdl,
                               CTranscodingPipeline *pParentPipeline,
                               SafetySurfaceBuffer  *pBuffer,
                               BitstreamProcessor   *pBSProc,
                               mfxVersion version);

        // frames allocation is suspended for heterogeneous pipeline
        virtual mfxStatus CompleteInit();
        virtual void      Close();
        virtual mfxStatus Join(MFXVideoSession *pChildSession);
        virtual mfxStatus Run();
        virtual mfxStatus FlushLastFrames(){return MFX_ERR_NONE;}
    
        mfxU32 GetProcessFrames() {return m_nProcessedFramesNum;}

        bool   GetJoiningFlag() {return m_bIsJoinSession;}
        
        mfxStatus QueryMFXVersion(mfxVersion *version) 
        { MSDK_CHECK_POINTER(m_pmfxSession.get(), MFX_ERR_NULL_PTR); return m_pmfxSession->QueryVersion(version); };

    protected:
        virtual mfxStatus CheckExternalBSProcessor(BitstreamProcessor   *pBSProc);
        
        virtual mfxStatus Decode();
        virtual mfxStatus Encode();
        virtual mfxStatus Transcode();
        virtual mfxStatus DecodeOneFrame(ExtendedSurface *pExtSurface);
        virtual mfxStatus DecodeLastFrame(ExtendedSurface *pExtSurface);
        virtual mfxStatus VPPOneFrame(mfxFrameSurface1 *pSurfaceIn, ExtendedSurface *pExtSurface);
        virtual mfxStatus EncodeOneFrame(ExtendedSurface *pExtSurface, mfxBitstream *pBS);
        
        virtual mfxStatus DecodePreInit(sInputParams *pParams);
        virtual mfxStatus VPPPreInit(sInputParams *pParams);
        virtual mfxStatus EncodePreInit(sInputParams *pParams);

        mfxVideoParam GetDecodeParam() const {return m_mfxDecParams;};
        mfxExtOpaqueSurfaceAlloc GetDecOpaqueAlloc() const {return m_DecOpaqueAlloc;};
        mfxExtMVCSeqDesc GetDecMVCSeqDesc() const {return m_MVCSeqDesc;};
    
        // alloc frames for all component        
        mfxStatus AllocFrames(mfxFrameAllocRequest  *pRequest, bool isDecAlloc);        
        mfxStatus AllocFrames();
        // need for heterogeneous pipeline
        mfxStatus CalculateNumberOfReqFrames(mfxFrameAllocRequest  *pRequest);
        void      CorrectNumberOfAllocatedFrames(mfxFrameAllocRequest  *pRequest);
        void      FreeFrames();

        mfxFrameSurface1* GetFreeSurface(bool isDec);

        // parameters configuration functions
        mfxStatus InitDecMfxParams(sInputParams *pInParams);
        mfxStatus InitVppMfxParams(sInputParams *pInParams);
        mfxStatus InitEncMfxParams(sInputParams *pInParams);
        mfxStatus InitPluginMfxParams(sInputParams *pInParams);
        mfxStatus AllocAndInitVppDoNotUse();
        mfxStatus AllocMVCSeqDesc(); 
        mfxStatus InitOpaqueAllocBuffers();

        void FreeVppDoNotUse(); 
        void FreeMVCSeqDesc();            
                
        mfxStatus AllocateSufficientBuffer(mfxBitstream* pBS);
        mfxStatus PutBS();

        void NoMoreFramesSignal(ExtendedSurface &DecExtSurface);

        mfxBitstream        *m_pmfxBS;  // contains encoded input data 

        std::auto_ptr<MFXVideoSession>  m_pmfxSession;
        std::auto_ptr<MFXVideoDECODE>   m_pmfxDEC;
        std::auto_ptr<MFXVideoENCODE>   m_pmfxENC;
        std::auto_ptr<MFXVideoMultiVPP> m_pmfxVPP; // either VPP or VPPPlugin which wraps [VPP]-Plugin-[VPP] pipeline

        mfxFrameAllocResponse          m_mfxDecResponse;  // memory allocation response for decoder          
        mfxFrameAllocResponse          m_mfxEncResponse;  // memory allocation response for encoder                            

        MFXFrameAllocator              *m_pMFXAllocator;
        void*                           m_hdl; // Diret3D device manager

        typedef std::vector<mfxFrameSurface1*> SurfPointersArray;
        SurfPointersArray  m_pSurfaceDecPool;
        SurfPointersArray  m_pSurfaceEncPool;
        mfxU16 m_EncSurfaceType; // actual type of encoder surface pool
        mfxU16 m_DecSurfaceType; // actual type of decoder surface pool


		// =========== ffmpeg muxer integration ============
		mfxU8 SPSBuffer[MAXSPSPPSBUFFERSIZE];
		mfxU8 PPSBuffer[MAXSPSPPSBUFFERSIZE];
		// =========== ffmpeg muxer integration end ============

        // transcoding pipeline specific
        typedef std::list<ExtendedBS*>       BSList;
        BSList  m_BSPool;

        mfxVideoParam                  m_mfxDecParams; 
        mfxVideoParam                  m_mfxEncParams; 
        mfxVideoParam                  m_mfxVppParams;
        mfxVideoParam                  m_mfxPluginParams;
        bool                           m_bIsVpp; // true if there's VPP in the pipeline
        bool                           m_bIsPlugin; //true if there's Plugin in the pipeline
        RotateParam                    m_RotateParam;

        // various external buffers
        // for disabling VPP algorithms
        mfxExtVPPDoNotUse m_VppDoNotUse; 
        // for MVC decoder and encoder configuration
        mfxExtMVCSeqDesc m_MVCSeqDesc; 
        bool m_bOwnMVCSeqDescMemory; // true if the pipeline owns memory allocated for MVCSeqDesc structure fields
        // for opaque memory
        mfxExtOpaqueSurfaceAlloc m_EncOpaqueAlloc;
        mfxExtOpaqueSurfaceAlloc m_VppOpaqueAlloc;
        mfxExtOpaqueSurfaceAlloc m_DecOpaqueAlloc;
        mfxExtOpaqueSurfaceAlloc m_PluginOpaqueAlloc;

        // external parameters for each component are stored in a vector
        std::vector<mfxExtBuffer*> m_VppExtParams;
        std::vector<mfxExtBuffer*> m_EncExtParams;
        std::vector<mfxExtBuffer*> m_DecExtParams;                
        std::vector<mfxExtBuffer*> m_PluginExtParams;                

        mfxU16         m_AsyncDepth;
        mfxU32         m_nProcessedFramesNum;

        bool           m_bIsJoinSession;

        bool           m_bDecodeEnable;
        bool           m_bEncodeEnable;

        SafetySurfaceBuffer   *m_pBuffer;
        CTranscodingPipeline  *m_pParentPipeline;

        mfxFrameAllocRequest   m_Request;
        bool                   m_bIsInit;

        std::auto_ptr<ExtendedBSStore>        m_pBSStore;

        mfxU32                                m_MaxFramesForTranscode;

        // pointer to already extended bs processor
        BitstreamProcessor                   *m_pBSProcessor;
        
        bool m_bUseOpaqueMemory; // indicates if opaque memory is used in the pipeline        

    private:
        DISALLOW_COPY_AND_ASSIGN(CTranscodingPipeline);

    };

    struct ThreadTranscodeContext
    {
        // Pointer to the session's pipeline
        std::auto_ptr<CTranscodingPipeline> pPipeline;
        // Pointer to bitstream handling object
        BitstreamProcessor *pBSProcessor;
        // Session implementation type
        mfxIMPL implType;

        // Session's starting status
        mfxStatus startStatus;
        // Session's working time
        mfxF64 working_time;

        // Number of processed frames
        mfxU32 numTransFrames;
        // Status of the finished session
        mfxStatus transcodingSts;
    };

   
}

// =========== ffmpeg splitter integration ============

// Disable type conversion warning to remove the annoying complaint about the ffmpeg "libavutil\common.h" file
#pragma warning( disable : 4244 )

#ifdef __cplusplus

#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
#include <stdint.h>

extern "C"
{
#endif

#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"

#ifdef __cplusplus
} // extern "C"
#endif

// If set, decodes audio stream into raw PCM samples
#define DECODE_AUDIO
#define ENCODE_AUDIO
#define REALTIME_DELAY

class FFMPEGReader : public CSmplBitstreamReader
{
public :

	FFMPEGReader();
	virtual ~FFMPEGReader();

	virtual void      Reset();
	virtual void      Close();
	virtual mfxStatus Init(const TCHAR *strFileName, mfxU32 videoType);
	virtual mfxStatus ReadNextFrame(mfxBitstream *pBS);

protected:
	bool						m_bInited;

	mfxU32						m_videoType;
	AVFormatContext*			m_pFormatCtx;
	int							m_videoStreamIdx;
	AVBitStreamFilterContext*	m_pBsfc;
#ifdef DECODE_AUDIO
	int							m_audioStreamIdx;
#endif
};

// =========== ffmpeg splitter integration end ============
// =========== ffmpeg muxer integration ============


class FFMPEGWriter: public CSmplBitstreamWriter
{
public :
	FFMPEGWriter();
	virtual ~FFMPEGWriter();

	virtual mfxStatus Init(const TCHAR *strFileName, TranscodingSample::sInputParams* pParams, mfxU8* SPSbuf, int SPSbufsize, mfxU8* PPSbuf, int PPSbufsize);
	virtual mfxStatus WriteNextFrame(mfxBitstream *pMfxBitstream, bool isPrint = true);
	virtual void Close();

protected:
	mfxU32				m_nProcessedFramesNum;
	bool				m_bInited;    

	AVOutputFormat*		m_pFmt;
	AVFormatContext*	m_pFormatCtx;
	AVStream*			m_pVideoStream;
	mfxU8*				m_pExtDataBuffer;	 
};

// =========== ffmpeg muxer integration end ============

#endif 
